package com.zzyl.nursing.service.impl;

import java.time.*;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

import com.zzyl.common.exception.base.BaseException;
import com.zzyl.nursing.dto.CreateReservationDto;
import com.zzyl.nursing.dto.ReservationCountDto;
import com.zzyl.nursing.vo.TimeSlotAvailabilityVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import com.zzyl.nursing.mapper.ReservationMapper;
import com.zzyl.nursing.domain.Reservation;
import com.zzyl.nursing.service.IReservationService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.transaction.annotation.Transactional;


/**
 * 预约信息Service业务层处理
 *
 * @author ck
 * @date 2025-06-10
 */
@Service
@Slf4j
public class ReservationServiceImpl extends ServiceImpl<ReservationMapper, Reservation> implements IReservationService {
    @Autowired
    private ReservationMapper reservationMapper;

    /**
     * 查询预约信息
     *
     * @param id 预约信息主键
     * @return 预约信息
     */
    @Override
    public Reservation selectReservationById(Long id) {
        return reservationMapper.selectById(id);
    }

    /**
     * 查询预约信息列表
     *
     * @param reservation 预约信息
     * @return 预约信息
     */
    @Override
    public List<Reservation> selectReservationList(Reservation reservation) {
        return reservationMapper.selectReservationList(reservation);
    }

    /**
     * 新增预约信息
     *
     * @param reservation 预约信息
     * @return 结果
     */
    @Override
    public int insertReservation(Reservation reservation) {
        return reservationMapper.insert(reservation);
    }

    /**
     * 修改预约信息
     *
     * @param reservation 预约信息
     * @return 结果
     */
    @Override
    public int updateReservation(Reservation reservation) {
        return reservationMapper.updateById(reservation);
    }

    /**
     * 批量删除预约信息
     *
     * @param ids 需要删除的预约信息主键
     * @return 结果
     */
    @Override
    public int deleteReservationByIds(Long[] ids) {
        return reservationMapper.deleteBatchIds(Arrays.asList(ids));
    }

    /**
     * 删除预约信息信息
     *
     * @param id 预约信息主键
     * @return 结果
     */
    @Override
    public int deleteReservationById(Long id) {
        return reservationMapper.deleteById(id);
    }

    /**
     * 查询取消预约数量
     */
    @Override
    public int cancelCount() {
        //根据id
        int count = reservationMapper.cancelCount();
        if (count > 0) {
            return count;
        }
        return 1;
    }

    @Override
    public void cancel(Long id) {

    }


    // 最大预约次数配置（可配置化）
    private static final int MAX_RESERVATIONS_PER_SLOT = 6;

    // 每日工作时间段配置（可配置化）
    private static final List<LocalTime> TIME_SLOTS = Arrays.asList(
            LocalTime.of(8, 0), LocalTime.of(8, 30),
            LocalTime.of(9, 0), LocalTime.of(9, 30),
            LocalTime.of(10, 0), LocalTime.of(10, 30),
            LocalTime.of(11, 0), LocalTime.of(11, 30),
            LocalTime.of(12, 0), LocalTime.of(12, 30),
            LocalTime.of(13, 0), LocalTime.of(13, 30),
            LocalTime.of(14, 0), LocalTime.of(14, 30),
            LocalTime.of(15, 0), LocalTime.of(15, 30),
            LocalTime.of(16, 0), LocalTime.of(16, 30),
            LocalTime.of(17, 0), LocalTime.of(17, 30)
    );


    @Override
    public List<TimeSlotAvailabilityVo> countAvailabilityByTime(Long timestamp) {
        // 1. 将时间戳转换为LocalDate（日期部分）
        LocalDate date = Instant.ofEpochMilli(timestamp)
                .atZone(ZoneId.systemDefault())
                .toLocalDate();

        // 2. 获取当天所有时间段的预约统计（只包含有效预约）
        Map<String, Integer> reservationCounts = getReservationCountsForDate(date);

        // 3. 计算每个时间段的剩余预约次数
        List<TimeSlotAvailabilityVo> result = new ArrayList<>();

        for (LocalTime slot : TIME_SLOTS) {
            LocalDateTime dateTime = LocalDateTime.of(date, slot);
            String timeKey = dateTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:00"));

            int reservedCount = reservationCounts.getOrDefault(timeKey, 0);
            int remaining = MAX_RESERVATIONS_PER_SLOT - reservedCount;

            // 只返回剩余次数小于最大预约次数的时间段
            if (remaining < MAX_RESERVATIONS_PER_SLOT) {
                TimeSlotAvailabilityVo vo = new TimeSlotAvailabilityVo();
                vo.setTime(dateTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
                vo.setCount(remaining);
                result.add(vo);
            }
        }

        // 4. 按时间段排序
        result.sort(Comparator.comparing(TimeSlotAvailabilityVo::getTime));

        return result;
    }


    /**
     * 获取指定日期的预约统计（只统计有效预约）
     */
    private Map<String, Integer> getReservationCountsForDate(LocalDate date) {
        // 获取当天最早和最晚时间
        LocalDateTime start = date.atStartOfDay();
        LocalDateTime end = date.plusDays(1).atStartOfDay().minusNanos(1);

        // 查询数据库获取预约统计
        List<ReservationCountDto> counts = reservationMapper.countReservationsByTimeRange(start, end);

        // 转换为Map: time -> count
        return counts.stream()
                .collect(Collectors.toMap(
                        ReservationCountDto::getTime,
                        ReservationCountDto::getCount
                ));
    }

    @Autowired
    private RedisTemplate<String, String> redisTemplate; // 用于分布式锁
    /**
     * 新增预约
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void createReservation(CreateReservationDto dto) {
//        // 1. 校验预约时间是否有效（在工作时间段内）
//        validateReservationTime(dto.getTime());

//        // 2. 获取分布式锁（防止并发重复预约）
//        String lockKey = "reservation:lock:" + dto.getTime().format(DateTimeFormatter.ofPattern("yyyyMMddHHmm"));
//        boolean lockAcquired = false;
//        try {
//            // 尝试获取锁，超时时间5秒
//            lockAcquired = redisTemplate.opsForValue().setIfAbsent(lockKey, "locked", 5, TimeUnit.SECONDS);
//            if (!lockAcquired) {
//                throw new ServiceException("系统繁忙，请稍后再试");
//            }

            // 3. 校验时间段剩余预约次数
            checkAvailability(dto.getTime());

            // 4. 校验同一手机号在同一时间段是否已预约
            if (reservationMapper.existsByMobileAndTime(dto.getMobile(), dto.getTime())) {
                throw new BaseException("您已预约该时间段，请勿重复预约");
            }

            // 5. 构建预约实体
            Reservation reservation = new Reservation();
            reservation.setName(dto.getName());
            reservation.setMobile(dto.getMobile());
            reservation.setTime(dto.getTime());
            reservation.setVisitor(dto.getVisitor());
            reservation.setType(dto.getType());
            reservation.setStatus(0); // 0-待报道

            // 6. 保存预约
            if (reservationMapper.insert(reservation) <= 0) {
                throw new BaseException("预约失败，请重试");
            }

            log.info("预约成功：{} - {}", dto.getName(), dto.getTime());
//        } finally {
//            // 释放锁
//            if (lockAcquired) {
//                redisTemplate.delete(lockKey);
//            }
//        }
    }

//    /**
//     * 校验预约时间有效性
//     */
//    private void validateReservationTime(LocalDateTime time) {
//        // 预约时间必须大于当前时间+30分钟
//        LocalDateTime minTime = LocalDateTime.now().plusMinutes(30);
//        if (time.isBefore(minTime)) {
//            throw new ServiceException("请至少提前30分钟预约");
//        }
//
//        // 预约时间必须在工作时间内（08:00-17:30）
//        LocalTime appointmentTime = time.toLocalTime();
//        if (appointmentTime.isBefore(LocalTime.of(8, 0))) {
//            throw new ServiceException("预约时间不能早于08:00");
//        }
//        if (appointmentTime.isAfter(LocalTime.of(17, 30))) {
//            throw new ServiceException("预约时间不能晚于17:30");
//        }
//    }

    /**
     * 检查时间段剩余预约次数
     */
    private void checkAvailability(LocalDateTime time) {
        // 格式化为整点（去掉秒）
        LocalDateTime slotTime = time.withSecond(0).withNano(0);

        // 查询该时间段已有预约数
        Integer count = reservationMapper.countByTimeAndStatus(slotTime, 0);
        if (count != null && count >= MAX_RESERVATIONS_PER_SLOT) {
            throw new BaseException("该时间段已约满");
        }
    }
}

